package chess;

import java.util.*;

public class Board {

    private Square _matrix[][];
    private ArrayList<Square> _white; //element 0 is always the king
    private ArrayList<Square> _black;

    public Board() {
        init();
    }

    public Board(Board original) {
        this._matrix = new Square[8][8];
        this._white = new ArrayList<Square>();
        this._black = new ArrayList<Square>();
                

        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                _matrix[i][j] = new Square(original._matrix[i][j]);
            }
        }
                for (Square s : original._white) {
                    _white.add(_matrix[s.file()][s.rank()]);
                }
                for (Square s : original._black) {
                    _black.add(_matrix[s.file()][s.rank()]);
                }
    }

    public Square getSquare(int file, int rank) {
        return _matrix[file][rank];
    }

    //gets all unblocked moves for all pieces
    public ArrayList<Move> getAllUnblockedMoves(boolean black) {
        ArrayList<Square> side;
        ArrayList<Move> result = new ArrayList<Move>();
        if (black) {
            side = _black;
        } else {
            side = _white;
        }
        for (Square piece : side) {
                    result.addAll(getAllUnblockedMoves(piece));
        }
        return result;
    }

    //gets all unblocked moves for a single piece
    public ArrayList<Move> getAllUnblockedMoves(Square square) {
        int file = square.file();
        int rank = square.rank();
        ArrayList<Move> result = new ArrayList<Move>();
        if (square.piece().isEmpty()) {
            return result;
        }
        Move m;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                if (square.piece().canMoveGeometric(file, rank, i - file, j - rank, !_matrix[i][j].piece().isEmpty())) {
                    m = new Move(file, rank, i, j);
                    if (canMoveUnblocked(m)) {
                        result.add(m);
                    }
                }
             }
        }
        return result;
    }

    public boolean canMoveUnblocked(Move move) {
        Square piece = _matrix[move.fromFile()][move.fromRank()];
        Square dest = _matrix[move.toFile()][move.toRank()];
        Piece p = piece.piece();
        Piece destp = dest.piece();
        if ((destp.isBlack() && p.isBlack()) || (destp.isWhite() && p.isWhite())) { //cannot capture own piece
            return false;
        }

        if (!p.canMoveGeometric(move.fromFile(), move.fromRank(), move.dFile(), move.dRank(), !destp.isEmpty())) {
            //System.out.println(p);
            return false;
        }

        if (p.isBishop() || p.isRook() || p.isQueen() || p.isKing()) {
            return canMoveUnblockedLine(move);
        }
        return true;
    }

    public boolean canMoveUnblockedLine(Move move) {
        int fromFile = move.fromFile();
        int fromRank = move.fromRank();
        int toFile = move.toFile();
        int toRank = move.toRank();
        int dFile = toFile - fromFile;
        int dRank = toRank - fromRank;
        int signFile, signRank;

        if (dFile == 0) {
            signFile = 0;
        } else {
            signFile = dFile / Math.abs(dFile);
        }
        if (dRank == 0) {
            signRank = 0;
        } else {
            signRank = dRank / Math.abs(dRank);
        }

        for (   int i = signFile, j = signRank;
                fromFile + i != toFile || fromRank + j != toRank;
                i+=signFile, j +=signRank
            )
        {
            if (!_matrix[fromFile+i][fromRank+j].piece().isEmpty()) {
                return false;
            }
        }
        return true;
    }

    public void makeMove(Move move) {
        int fromFile = move.fromFile();
        int fromRank = move.fromRank();
        int toFile = move.toFile();
        int toRank = move.toRank();
        Square piece = _matrix[fromFile][fromRank];
        Square dest = _matrix[toFile][toRank];
        dest.setPiece(piece.piece());
        assert !(piece.piece().isEmpty());

        ArrayList<Square> mover;
        ArrayList<Square> other;

        if (piece.piece().isWhite()) {
            mover = _white;
            other = _black;
        } else {
            mover = _black;
            other = _white;
        }

        piece.setPiece(Piece.EMPTY);
        mover.remove(piece);

        if (dest.piece().isKing()) {
            mover.add(0, dest);
        } else {
            mover.add(dest);
        }

        if (!dest.piece().isEmpty()) {
            other.remove(dest);
        }

        //moving the rook during castling
        if (dest.piece().isKing()) {
            if (toFile - fromFile == 2) {
                _matrix[toFile - 1][toRank].setPiece(_matrix[toFile + 1][toRank].piece());
                _matrix[toFile + 1][toRank].setPiece(Piece.EMPTY);
            } else if (fromFile - toFile == 2) {
                _matrix[toFile + 1][toRank].setPiece(_matrix[toFile - 2][toRank].piece());
                _matrix[toFile - 2][toRank].setPiece(Piece.EMPTY);
            }
        }
    }

    public boolean isInCheck(boolean black) {
        ArrayList<Square> attacker;
        Square king;
        if (black) {
            attacker = _white;
            king = _black.get(0);
        } else {
            attacker = _black;
            king = _white.get(0);
        }

        ArrayList<Move> moves;
        for (Square piece : attacker) {
            /*moves = getAllUnblockedMoves(piece);
            for (Move move : moves) {
                System.out.println(move);
                if (move.toFile() == king.file() && move.toRank() == king.rank()) {
                    return true;
                }
            }*/
            Move m = new Move(piece.file(), piece.rank(), king.file(), king.rank());
            if (canMoveUnblocked(m)) {
                return true;
            }
        }
        return false;
    }

    public boolean isInCheckmate(boolean black) {
        if (!isInCheck(black)) {
            return false;
        }
        return false;
    }

        public ArrayList<Square> white() {
            return _white;
        }

        public ArrayList<Square> black() {
            return _black;
        }

    public String toString() {
        String result = "";
        for (int j = 7; j >= 0; j--) {
            for (int i = 0; i < 8; i++) {
                result += _matrix[i][j].toString();
            }
            result += "\n";
        }
        return result;
    }

    private void init() {
        _matrix = new Square[8][8];
        _white = new ArrayList<Square>();
        _black = new ArrayList<Square>();

        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                _matrix[i][j] = new Square(i, j, Piece.EMPTY);
            }
        }

        _matrix[0][0].setPiece(Piece.ROOK_WHITE);
        _matrix[1][0].setPiece(Piece.KNIGHT_WHITE);
        _matrix[2][0].setPiece(Piece.BISHOP_WHITE);
        _matrix[3][0].setPiece(Piece.QUEEN_WHITE);
        _matrix[4][0].setPiece(Piece.KING_WHITE);
        _matrix[5][0].setPiece(Piece.BISHOP_WHITE);
        _matrix[6][0].setPiece(Piece.KNIGHT_WHITE);
        _matrix[7][0].setPiece(Piece.ROOK_WHITE);
        _matrix[0][1].setPiece(Piece.PAWN_WHITE);
        _matrix[1][1].setPiece(Piece.PAWN_WHITE);
        _matrix[2][1].setPiece(Piece.PAWN_WHITE);
        _matrix[3][1].setPiece(Piece.PAWN_WHITE);
        _matrix[4][1].setPiece(Piece.PAWN_WHITE);
        _matrix[5][1].setPiece(Piece.PAWN_WHITE);
        _matrix[6][1].setPiece(Piece.PAWN_WHITE);
        _matrix[7][1].setPiece(Piece.PAWN_WHITE);
        _matrix[0][7].setPiece(Piece.ROOK_BLACK);
        _matrix[1][7].setPiece(Piece.KNIGHT_BLACK);
        _matrix[2][7].setPiece(Piece.BISHOP_BLACK);
        _matrix[3][7].setPiece(Piece.QUEEN_BLACK);
        _matrix[4][7].setPiece(Piece.KING_BLACK);
        _matrix[5][7].setPiece(Piece.BISHOP_BLACK);
        _matrix[6][7].setPiece(Piece.KNIGHT_BLACK);
        _matrix[7][7].setPiece(Piece.ROOK_BLACK);
        _matrix[0][6].setPiece(Piece.PAWN_BLACK);
        _matrix[1][6].setPiece(Piece.PAWN_BLACK);
        _matrix[2][6].setPiece(Piece.PAWN_BLACK);
        _matrix[3][6].setPiece(Piece.PAWN_BLACK);
        _matrix[4][6].setPiece(Piece.PAWN_BLACK);
        _matrix[5][6].setPiece(Piece.PAWN_BLACK);
        _matrix[6][6].setPiece(Piece.PAWN_BLACK);
        _matrix[7][6].setPiece(Piece.PAWN_BLACK);

        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 2; j++) {
                if (i == 4 && j == 0) {
                    _white.add(0, _matrix[i][j]);
                    _black.add(0, _matrix[i][7-j]);
                } else {
                    _white.add(_matrix[i][j]);
                    _black.add(_matrix[i][7-j]);
                }
            }
        }
    }
}
